[Frida 4] Frida JS API 사용법 정리 - Part 2

[Frida 4] Frida JS API 사용법 정리 - Part 2

Lecture
Security
태그
frida
mobile hacking
public
완성
Y
생성일
Mar 18, 2024 05:48 AM
LectureName
Mobile Hacking (Android - Frida )

1. Frida JS API - JAVA.


Frida-JS API에서 Java.로 시작하는 API는 안드로이드 애플리케이션의 Java 런타임 환경을 조작하고 상호 작용하기 위한 기능을 제공합니다.
 
 

1.1 Java.perform( function )

➡️ 소개
현재 Frida Client의 Thread가 기기 가상머신(VM)에 연결되어 있는지 확인하고 function 을 호출합니다. Frida를 사용하려면, 반드시 해당 메서드를 통해 단말기 연결을 확인하여야 합니다.
➡️ 사용
Java.perform(function() { ~ })
 
 

1.2 Java.use(className)

➡️ 소개
변수와 메서드에 엑세스 할 수 있는 클래스 객체 를 반환합니다. (클래스가 구현되어 있는 인스턴스를 반환하는 것이 아닌 클래스의 객체를 반환합니다.) 클래스 객체를 반환받아 해당 클래스에 대한 method를 재 작성할 수 있습니다.
 
➡️ 사용
Java.perform(function() { var <변수> = Java.use(<클래스 경로>) //ex com.android.insecurebankv2 //method를 새로 작성 <변수>.<클래스에 존재하는 메서드>.implementation = function(param) { //작성 } } //example Java.perform(function() { var doLogin = Java.use(com.anroid.insecurebank.DoLogin) //method를 새로 작성 doLogin.checkPassword.implementation = function(param) { //작성 } }
 
 

1.3 Java.choose

➡️ 소개
Java.use() 가 클래스를 반환했다면, Java.choose 는 현재 힙에 올라와 있는 인스턴스화 된 객체를 찾을 수 있습니다.
 
➡️ 사용
Java.choose(className, callbacks)
Java.perform(function() { Java.choose(<클래스 경로>, { //callback "onMatch" : function(instance){ console.log(instance.toString()) } //callback "onComplete" : function() {} }) })
  • onMatch : 실시간으로 인스턴스에 대해 호출합니다.
  • onComplete : 함수가 클래스 내의 모든 인스턴스를 확인한 후에 호출됩니다.
 
 

1.4 Java.enumerateLoadedClasses(callbaks)

➡️ 소개
애플리케이션이 실행중일때, 클래스는 메모리에 동적으로 로드됩니다. 이때 Java.enumerateLoadedClasses 를 사용하면 로드된 모든 클래스를 조회하고, 매개변수에 따라 일치 항목을 추출할 수 있습니다.
 
➡️ 사용
Java.enumerateLoadedClasses( callbacks )
Java.perform(function() { Java.enumeratLoadedClasses({ "onMatch" : function(className) { console.log(className) }, "onComplete":function() {} }) })
  • onMatch 에서는 className을 인자로 받아 일치하는 className을 출력할 수 있다.
 
 

1.5 .overload

➡️ 소개
Frida의 Java.use()를 사용하여 클래스에 접근하고, 해당 클래스를 오버로딩 할 수 있습니다.
 
➡️ 사용
// GPT Example Java.perform(function() { var ExampleClass = Java.use('com.example.ExampleClass'); // sendMessage 메서드의 시그니처를 지정하여 후킹 // 오버로딩된 메서드 중에서 우리가 원하는 시그니처를 선택 ExampleClass.sendMessage.overload('java.lang.String').implementation = function(message) { // 메서드가 호출될 때 실행되는 코드 console.log('sendMessage with String argument:', message); // 오버로딩된 메서드를 호출하고 반환 값을 가져올 수도 있음 return this.sendMessage.overload('java.lang.String').call(this, message); }; ExampleClass.sendMessage.overload('int').implementation = function(value) { // 메서드가 호출될 때 실행되는 코드 console.log('sendMessage with int argument:', value); // 오버로딩된 메서드를 호출하고 반환 값을 가져올 수도 있음 return this.sendMessage.overload('int').call(this, value); }; });
 
➡️ 매개변수 부여 예시
.overload() .overload('int’) .overload('boolean') .overload('float') .overload('[B') // byte array .overload('[B', '[B', '[B') .overload('java.io.File') .overload('java.lang.String') .overload('android.view.View') .overload('android.app.Activity') .overload('android.content.Context') .overload('android.content.Context', 'java.lang.String') .overload('java.lang.String', 'java.lang.String') .overload('android.graphics.Bitmap') .overload('java.util.List’) .overload('android.content.Context', 'java.lang.String', 'java.lang.String') .overload('android.app.Activity', 'int', 'int', 'int', 'boolean')
 
 
💡
  • Java.perform 등 메서드를 사용하기 전에 setImmediate 메서드를 사용하면 스크립트가 동작하는데 너무 오래걸려서 프리다가 시간 초과로 종료되는 것을 방지할 수 있습니다
  • 예시
setImmediate(function() { Java.perform(function() { ~ } }
 
 
⚠️ 주의 사항
함수를 후킹하게 되면, 비정상적으로 종료되지 않도록 기존의 함수 로직을 이어서 작성하거나, 정상적인 형식으로 재 작성하여 반환하여야 합니다
Java.perform(function(){ var class123 = Java.use("android.app.insecurebankv2") class123.<method>.implementation = function() { this.method(); } })
  • 예를 들어 원래 <method> 부분의 return이 Boolean 이라면 True / False 둘중 하나는 반환해야 한다는 이야기 입니다.
 
 

2. Frida JS API - Interceptor


Interceptor은 말 그대로 함수가 호출되면 해당 함수의 흐름을 변경하거나, 임의의 코드를 실행하고, 호출되는 과정에서 정보를 조회할 수 있는 분석 도구로 사용됩니다.
JavaAPI는 런타임 환경에서 Java method를 후킹하는 작업을 수행습니다. 하지만 해당 분석 도구는 네이티브 코드에서 시스템 호출(open, write 등)을 사용하는 경우에 해당 함수를 후킹하여 원하는 동작을 추가하거나 수정할 수 있습니다. (NativcePointer 인터셉터)
 
함수
함수
설명
Interceptor.attach(module, callbacks[, data])
module에 해당하는 함수가 호출되면 등록한 콜백에 따라 임의 코드를 실행합니다. 함수의 인자를 조회하거나 조작 등 실행 흐름을 파악할 수 있습니다.
Interceptor.replace(module, replacement[, data])
module 에 해당하는 함수가 호출되면 함수의 기능을 실행하지 않고 미리 정의된 임의 코드를 실행합니다.
  • 여기서 module 은 함수의 주소 값을 나타냅니다. 보통 Module.getExportByName() 을 이용하여 구합니다.
 
 
예시 1 - 함수 후킹
Interceptor.attach(Module.getExportByName('libc.so', 'read'), { onEnter(args) { this.fileDescriptor = args[0].toInt32(); }, onLeave(retval) { if (retval.toInt32() > 0) { /* do something with this.fileDescriptor */ } } });
  • libc.so 파일의 read 함수를 후킹합니다.
  • onEnter은 해당 read 함수를 찾은 경우 args 를 인자로 설정합니다.
  • onLeave 은 read가 호출이 끝날 경우 실행되며, retval은 returnValue 를 의미합니다.
 
 
예시 2 - 함수 후킹 및 trace
let open = Module.findExportByName(null, "open"); Interceptor.attach(open, { onEnter(args) { console.log('called from:\n' + Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n'); }, onLeave(retval) { console.log("ret: " + retval); } });